Skip to content

zix: serve the mounted certificate, drop upload, re-enable - #1303

Merged
MDA2AV merged 3 commits into
mainfrom
fix/zix-mounted-certs
Aug 24, 2026
Merged

zix: serve the mounted certificate, drop upload, re-enable#1303
MDA2AV merged 3 commits into
mainfrom
fix/zix-mounted-certs

Conversation

@MDA2AV

@MDA2AV MDA2AV commented Aug 24, 2026

Copy link
Copy Markdown
Owner

Every zix Dockerfile generated its own self-signed Ed25519 certificate at image build and baked it at /etc/zix-tls:

# Self-signed Ed25519 cert generated at image build, baked at /etc/zix-tls.
RUN openssl genpkey -algorithm ED25519 -out /etc/zix-tls/server.key; \
    openssl req -new -x509 -key /etc/zix-tls/server.key ...

Nothing in any of the five entries read the shared /certs mount. So every zix TLS listener — json-tls, static-tls, baseline-h2, static-h2 and unary-grpc-tls — signed its handshakes with an EC key while every other entry signs with the shared RSA-2048 pair. RSA-2048 signing is roughly 25× more expensive, so those handshakes were cheaper than the ones they were being compared against.

1. Serve the mounted certificate

Point the four TLS path constants at /certs/server.crt / /certs/server.key and drop the cert-generation stage from all five Dockerfiles. zix-ws generated a certificate it never even copied into its runtime image — that goes too.

No harness change needed: validate.sh already mounts /certs for any h1-TLS, h2, h3 or grpc-tls subscriber, and framework.sh mounts it unconditionally for benchmarks.

entry before after
zix 68 pass / 3 fail 70 pass / 1 fail
zix-grpc 4 pass / 0 fail 9 pass / 0 fail
zix-http2 23 pass / 2 fail 25 pass / 0 fail
zix-ws 7 pass / 0 fail 7 pass / 0 fail
zix-http3 covered by #1304

The served certificate now matches certs/server.crt by fingerprint (6E:00:1C:1F:…) on every listener. zix-grpc gains 5 checks because its TLS port had no posture probe at all — that gap is closed in #1304, and it passes there only because of this fix.

2. Drop upload, re-enable the entry

That left one failure, POST /upload chunked, and it is not the entry's to fix. zix.Http1 requires a chunked body to fit in a single receive buffer (.max_recv_buf, 8 KiB) while the Content-Length path drains correctly to 20 MB. Measured:

body Content-Length chunked
8000 200 200
8192 200 connection dropped
8193 – 1 MB 200 413
20 MB 200 413

Raising max_recv_buf to 256 K makes 112 KB pass and 1 MB still fail, confirming that buffer is the binding constraint. It is per-connection, so sizing it to the 24 MB max_request_body is not viable at these connection counts — and raising it just far enough to clear the validator's probe would game the check rather than fix the engine, while costing memory score.

upload is engineScored=False, so zix is an engine entry that loses no score by unsubscribing. The /upload handler stays in the source; it works correctly for Content-Length bodies and is simply no longer a subscribed profile.

zix now validates clean — 66 passed, 0 failed — and enabled goes back to true.

Note on the result file

site/data/results/zix.json does not exist on main; #1291 dropped it along with the entry. This PR does not restore numbers — #1221 (prothegee) is the re-entry that does, and it currently conflicts because of that same deletion and needs a rebase.

🤖 Generated with Claude Code

@github-actions

Copy link
Copy Markdown
Contributor

👋 Heads up! This PR modifies the following frameworks:

@MDA2AV MDA2AV changed the title zix: serve the mounted certificate instead of a baked Ed25519 one zix: serve the mounted certificate, drop upload, re-enable Aug 24, 2026
@MDA2AV
MDA2AV force-pushed the fix/zix-mounted-certs branch from 913f9c0 to c8bcb8e Compare August 24, 2026 17:59
MDA2AV added 2 commits August 24, 2026 19:47
Every zix Dockerfile generated its own self-signed Ed25519 certificate at
image build and baked it at /etc/zix-tls; nothing in any of the five entries
read the shared /certs mount. So every zix TLS listener -- json-tls,
static-tls, baseline-h2, static-h2 and unary-grpc-tls -- signed its handshakes
with an EC key while every other entry signs with the shared RSA-2048 pair.
RSA-2048 signing is roughly 25x more expensive than an EC key, so those
handshakes were cheaper than the ones they were being compared against.

Point the four TLS path constants at /certs/server.crt and /certs/server.key
and drop the cert-generation stage from all five Dockerfiles. zix-ws generated
a certificate it never even copied into its runtime image; that goes too.

validate.sh already mounts /certs whenever an entry subscribes to an h1-TLS,
h2, h3 or grpc-tls profile, and the benchmark harness mounts it
unconditionally, so nothing else has to change.

Verified by rebuilding all five and re-validating each:

  zix         68 pass / 3 fail  ->  70 pass / 1 fail
  zix-grpc     4 pass / 0 fail  ->   9 pass / 0 fail
  zix-http2   23 pass / 2 fail  ->  25 pass / 0 fail
  zix-ws       7 pass / 0 fail  ->   7 pass / 0 fail
  zix-http3    no coverage exists for h3 -- see the validation PR

The served certificate now matches certs/server.crt by fingerprint on every
listener. zix's one remaining failure is POST /upload chunked, which is
unrelated to TLS and is engine-level: zix.Http1 requires a chunked body to fit
in a single receive buffer (.max_recv_buf, 8 KiB) while the Content-Length
path drains correctly to 20 MB. Raising that buffer fixes the check but it is
per-connection memory, so it is an upstream fix rather than an entry one.
With the certificate fix the only remaining failure was POST /upload chunked,
and that one is not the entry's to fix: zix.Http1 requires a chunked body to
fit in a single receive buffer (.max_recv_buf, 8 KiB) while the Content-Length
path drains correctly to 20 MB. Measured: chunked succeeds to exactly 8192
bytes, drops the connection at the boundary, and returns 413 from 8193 up;
raising max_recv_buf to 256 K makes 112 KB pass and 1 MB still fail. That
buffer is per-connection, so sizing it to the 24 MB max_request_body is not
viable at these connection counts.

Unsubscribing is the honest option -- the alternative is raising the buffer far
enough to clear the validator's probe, which games the check rather than
fixing the engine, and costs memory score while doing it.

upload is engineScored=False, so zix is an engine entry that loses no score
by dropping it.

The /upload handler stays in the source; it works correctly for
Content-Length bodies and is simply no longer a subscribed profile.

zix now validates clean and comes back on: 66 passed, 0 failed.
@MDA2AV
MDA2AV force-pushed the fix/zix-mounted-certs branch from c8bcb8e to a11c1c1 Compare August 24, 2026 18:47
@MDA2AV

MDA2AV commented Aug 24, 2026

Copy link
Copy Markdown
Owner Author

/benchmark-multiple --save

@github-actions

Copy link
Copy Markdown
Contributor

👋 Benchmark request received. A collaborator will review and approve the run.

@github-actions

Copy link
Copy Markdown
Contributor

Benchmark Results

Frameworks: 5 | Test: all tests

zix

Test Conn RPS CPU Mem Δ RPS Δ Mem
baseline 512 4,188,550 6361.8% 133MiB ~0% ~0%
baseline 4096 4,442,329 6427.0% 194MiB ~0% ~0%
pipelined 512 56,503,100 6436.5% 132MiB ~0% ~0%
pipelined 4096 58,761,616 6408.9% 193MiB ~0% ~0%
limited-conn 512 2,683,418 5513.5% 131MiB ~0% ~0%
limited-conn 4096 2,708,192 5826.4% 236MiB ~0% ~0%
json 4096 2,325,200 6534.2% 287MiB ~0% ~0%
json-comp 512 955,775 6059.4% 132MiB ~0% ~0%
json-comp 4096 1,046,581 6825.8% 212MiB ~0% ~0%
json-comp 16384 1,018,808 6533.4% 527MiB ~0% ~0%
json-tls 4096 1,845,248 5396.1% 280MiB ~0% ~0%
api-4 256 62,678 199.8% 40MiB ~0% ~0%
api-16 1024 247,788 1097.0% 132MiB ~0% ~0%
static 1024 222,558 6658.0% 456MiB ~0% ~0%
static 4096 233,973 6617.6% 1.4GiB ~0% ~0%
static 6800 231,573 6568.0% 2.3GiB ~0% ~0%
static-tls 1024 339,689 6603.4% 228MiB ~0% ~0%
static-tls 4096 314,396 6492.3% 543MiB ~0% ~0%
static-tls 6800 300,828 6496.8% 841MiB ~0% ~0%
async-db 1024 400,444 3183.3% 337MiB ~0% ~0%
crud 4096 823,462 3213.5% 387MiB ~0% ~0%

zix-grpc

Test Conn RPS CPU Mem Δ RPS Δ Mem
unary-grpc 256 7,000,555 2791.2% 180MiB ~0% ~0%
unary-grpc 1024 7,030,197 2904.3% 206MiB ~0% ~0%
unary-grpc-tls 256 6,962,147 2903.7% 201MiB ~0% ~0%
unary-grpc-tls 1024 6,781,146 2877.7% 318MiB ~0% ~0%

zix-http2

Test Conn RPS CPU Mem Δ RPS Δ Mem
baseline-h2 256 14,076,613 4108.2% 120MiB ~0% ~0%
baseline-h2 1024 14,090,534 4164.7% 216MiB ~0% ~0%
static-h2 256 1,692,045 5935.6% 217MiB ~0% ~0%
static-h2 1024 1,650,640 6431.3% 563MiB ~0% ~0%
baseline-h2c 256 13,956,726 3999.4% 106MiB ~0% ~0%
baseline-h2c 1024 14,569,901 4171.5% 126MiB ~0% ~0%
baseline-h2c 4096 13,411,078 3900.5% 220MiB ~0% ~0%
json-h2c 1024 6,821,589 3891.6% 177MiB ~0% ~0%
json-h2c 4096 6,746,440 4204.8% 425MiB ~0% ~0%

zix-http3

Test Conn RPS CPU Mem Δ RPS Δ Mem
baseline-h3 64 9,086,240 1281.9% 412MiB ~0% ~0%
static-h3 64 595,586 3297.5% 412MiB ~0% ~0%

zix-ws

Test Conn RPS CPU Mem Δ RPS Δ Mem
echo-ws 512 4,277,156 6402.6% 206MiB ~0% ~0%
echo-ws 4096 4,469,914 6419.3% 251MiB ~0% ~0%
echo-ws 16384 4,254,843 6408.1% 487MiB ~0% ~0%
echo-ws-pipeline 512 60,193,061 6381.1% 211MiB ~0% ~0%
echo-ws-pipeline 4096 66,037,087 6239.8% 248MiB ~0% ~0%
echo-ws-pipeline 16384 61,907,676 6152.3% 497MiB ~0% ~0%
echo-ws-limited 512 2,254,504 5259.0% 176MiB ~0% ~0%
echo-ws-limited 4096 2,566,106 5761.4% 280MiB ~0% ~0%

@MDA2AV
MDA2AV merged commit d4731c3 into main Aug 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant